package com.example.chatgpt.fragment;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.chatgpt.ChatHistoryActivity;
import com.example.chatgpt.MyApplication;
import com.example.chatgpt.R;
import com.example.chatgpt.adapter.MessageAdapter;
import com.example.chatgpt.bean.ApiData;
import com.example.chatgpt.bean.Message;
import com.example.chatgpt.bean.User;
import com.example.chatgpt.dao.MessageDao;
import com.example.chatgpt.entity.MessageInfo;
import com.example.chatgpt.util.CloseKeyBoardUtil;
import com.example.chatgpt.util.TimeUtil;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class HomeFragment extends Fragment implements View.OnClickListener {
    private RecyclerView rv_message;
    private TextView tv_text;
    private EditText et_question;
    private List<Message> messageList;
    private MessageAdapter adapter;

    //导入okhttp
    public static final MediaType JSON
            = MediaType.get("application/json; charset=utf-8");
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .connectTimeout(40, TimeUnit.SECONDS)
            .writeTimeout(40, TimeUnit.SECONDS)
            .readTimeout(40, TimeUnit.SECONDS)
            .build();
    private String apiKey;
    private MessageDao messageDao;
    private EditText et_input_key;
    private SharedPreferences keyPreferences;
    private SharedPreferences.Editor keyEdit;

    //gpt模型选择标志
    private int flag = 0;

    //gpt模型数组
    private String[] modelArray = {
            "text-davinci-003", "gpt-3.5-turbo"
    };
    private ApiData apiData;
    private User userInfo;
    private MyApplication application;
    private TextView tv_model;

    @SuppressLint("MissingInflatedId")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_home, container, false);

        //获取viewModel
        /*viewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MessageViewModel.class);*/

        //获取控件
        rv_message = view.findViewById(R.id.rv_message);
        tv_text = view.findViewById(R.id.tv_text);
        et_question = view.findViewById(R.id.et_question);
        view.findViewById(R.id.ib_sent).setOnClickListener(this);
        view.findViewById(R.id.ib_delete).setOnClickListener(this);
        view.findViewById(R.id.ib_key).setOnClickListener(this);
        view.findViewById(R.id.ib_list).setOnClickListener(this);
        tv_model = view.findViewById(R.id.tv_model);
        view.findViewById(R.id.ib_select_model).setOnClickListener(this);

        //设置RecycleView的adapter
        messageList = new ArrayList<>();
        adapter = new MessageAdapter(messageList);
        rv_message.setLayoutManager(new LinearLayoutManager(getContext()));
        rv_message.setAdapter(adapter);

        //获取application实例
        application = (MyApplication) getActivity().getApplication();
        //获取数据库的dao对象
        messageDao = application.getMessageDao();
        //获取api参数对象
        apiData = application.getApiData();
        userInfo = application.getUserInfo();
        Log.d("guaimiao", String.valueOf(apiData.getMaxTokens()) + " " + String.valueOf(apiData.getTemperature()));

        Log.d("guaimiao", userInfo.getUser());

        //获取sharedPreference对象用于保存key
        keyPreferences = getContext().getSharedPreferences("api_key", Context.MODE_PRIVATE);
        keyEdit = keyPreferences.edit();

        //每次进入页面 加载上次输入的apikey
        reload();
        Log.d("guaimiao", apiKey);

        //设置当前模型
        if (!apiKey.equals("")) {
            tv_model.setText(modelArray[flag]);
        } else {
            tv_model.setText("您还没有设置apiKey!");
        }
        return view;
    }

    //加载apikey
    private void reload() {
        String api_key = keyPreferences.getString("api_key", "");
        if (!api_key.equals("")) {
            apiKey = api_key;
            //每次加载时都把apiKey放进apiData中 以便painting界面能够接收到
            //apiData.setApiKey(apiKey);
        } else {
            apiKey = "";
            //apiData.setApiKey("");
        }
    }

    //发送操作
    //清空操作
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.ib_sent:
                //关闭软键盘
                CloseKeyBoardUtil.hideOneInputMethod(getActivity(), view);
                //获取文本输入框中的问题
                String question = et_question.getText().toString().trim();
                //判空
                if (!apiKey.equals("")) {
                    if (question.equals("")) {
                        Toast.makeText(getContext(), "铁子 问题不能为空哦~", Toast.LENGTH_SHORT).show();
                        et_question.requestFocus();
                        return;
                    } else {
                        //将文本输入框中的问题加入到message列表 并且更新recycleView 展示
                        addToChat(question, Message.SENT_BY_ME);
                        //清空文本输入框
                        et_question.setText("");
                        //将提示文字隐藏
                        tv_text.setVisibility(View.GONE);
                        //请求ChatGPT
                        setCallApi(question, flag);
                    }
                } else {
                    Toast.makeText(getContext(), "请输入api-key!", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.ib_delete:
                deleteToChat();
                tv_text.setVisibility(View.VISIBLE);
                break;
            case R.id.ib_key:
                initKeyDialog();
                break;
            case R.id.ib_list:
                Intent intent1 = new Intent(getContext(), ChatHistoryActivity.class);
                intent1.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent1);
                break;
            case R.id.ib_select_model:
                initModelDialog();
                break;
        }

    }

    //gpt模型底部弹窗
    @SuppressLint("MissingInflatedId")
    private void initModelDialog() {
        Dialog bottomDialog = new Dialog(getContext(), R.style.BottomDialog);
        //获取布局文件
        View view = LayoutInflater.from(getContext()).inflate(R.layout.select_model, null);
        //加入布局
        bottomDialog.setContentView(view);
        //设置参数
        ViewGroup.LayoutParams params = view.getLayoutParams();
        params.width = getResources().getDisplayMetrics().widthPixels;
        bottomDialog.getWindow().setGravity(Gravity.BOTTOM);
        bottomDialog.getWindow().setWindowAnimations(R.style.BottomDialog_Animation);
        bottomDialog.show();
        ListView lv_model = view.findViewById(R.id.lv_model);
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getContext(), R.layout.item_moel, R.id.tv_model, modelArray);
        lv_model.setAdapter(arrayAdapter);
        lv_model.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
                switch (position) {
                    case 0:
                        //判断密钥是否为空 在设置flag的值 以及模型选择
                        if (!apiKey.equals("")) {
                            flag = 0;
                            tv_model.setText(modelArray[flag]);
                            Toast.makeText(getContext(), "您已选择" + modelArray[flag] + "模型", Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(getContext(), "您还没有设置apiKey!", Toast.LENGTH_SHORT).show();
                        }

                        break;
                    case 1:
                        if (!apiKey.equals("")) {
                            flag = 1;
                            tv_model.setText(modelArray[flag]);
                            Toast.makeText(getContext(), "您已选择" + modelArray[flag] + "模型", Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(getContext(), "您还没有设置apiKey!", Toast.LENGTH_SHORT).show();
                        }
                        break;
                }
                bottomDialog.dismiss();
            }
        });
    }

    //设置模型
    void setCallApi(String question, int flag) {
        switch (flag) {
            case 0:
                callApiDavinci(question);
                break;
            case 1:
                callApiTurbo(question);
                break;
        }
    }

    //密钥底部弹窗
    private void initKeyDialog() {
        //创建dialog对象
        Dialog bottomDialog = new Dialog(getContext(), R.style.BottomDialog);
        //获取布局文件
        View view = LayoutInflater.from(getContext()).inflate(R.layout.activity_input_api_key, null);
        //加入布局
        bottomDialog.setContentView(view);
        //设置参数
        ViewGroup.LayoutParams params = view.getLayoutParams();
        params.width = getResources().getDisplayMetrics().widthPixels;
        bottomDialog.getWindow().setGravity(Gravity.BOTTOM);
        bottomDialog.getWindow().setWindowAnimations(R.style.BottomDialog_Animation);
        bottomDialog.show();
        //事件处理
        et_input_key = view.findViewById(R.id.et_input_key);
        view.findViewById(R.id.ib_confirm).setOnClickListener(view1 -> {
            Toast.makeText(getContext(), "修改apiKey成功!", Toast.LENGTH_SHORT).show();
            apiKey = et_input_key.getText().toString().trim();
            //设置不同密钥状态下当前模型的显示
            if (apiKey.equals("")) {
                tv_model.setText("您还没有设置apiKey!");
            } else {
                tv_model.setText(modelArray[flag]);
            }
            //apiData.setApiKey(apiKey);
            //用sharedPreference保存key
            keyEdit.putString("api_key", apiKey);
            keyEdit.commit();
            bottomDialog.dismiss();
        });
    }

    //清空聊天信息
    void deleteToChat() {
        getActivity().runOnUiThread(() -> {
            messageList.removeAll(messageList);
            adapter.notifyDataSetChanged();
            rv_message.smoothScrollToPosition(adapter.getItemCount());
        });
    }

    //将信息装进messageList中 并更新recycleView
    void addToChat(String message, String sentBy) {
        getActivity().runOnUiThread(() -> {
            messageList.add(new Message(message, sentBy));
            //将消息加入到数据库
            messageDao.insertMessage(new MessageInfo(message, sentBy, TimeUtil.getDateNow(), userInfo.getUser()));
            //更新recycleView
            adapter.notifyDataSetChanged();
            rv_message.smoothScrollToPosition(adapter.getItemCount());
        });
    }

    void addResponse(String response) {
        messageList.remove(messageList.size() - 1);
        //将加入的请求骚等数据删除
        messageDao.deleteByMessage("正在请求 骚等~");
        addToChat(response, Message.SENT_BY_ROBOT);
    }

    //请求ChatGPT
    void callApiDavinci(String question) {

        addToChat("正在请求 骚等~", Message.SENT_BY_ROBOT);

        JSONObject jsonBody = new JSONObject();
        try {
            //使用模型
            jsonBody.put("model", "text-davinci-003");
            //问题关键字
            jsonBody.put("prompt", question);
            //回复的最大令牌数
            jsonBody.put("max_tokens", 4000);
            jsonBody.put("temperature", 0.7);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        //创建一个请求表单
        RequestBody requestBody = RequestBody.create(jsonBody.toString(), JSON);

        //创建request请求
        Request request = new Request.Builder()
                //请求地址
                .url("https://api.openai.com/v1/completions")
                //api key
                .header("Authorization", "Bearer " + apiKey)
                .addHeader("Content-Type", "application/json")
                //提交表单
                .post(requestBody)
                .build();

        //提交请求 得到response返回报文
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                addResponse("Failed to load response due to " + e.getMessage());
                Log.d("guaimiao", e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    try {
                        JSONObject jsonObject = new JSONObject(response.body().string());
                        Log.d("guaimiao", jsonObject.toString());
                        JSONArray jsonArray = jsonObject.getJSONArray("choices");
                        Log.d("guaimiao", jsonArray.toString());
                        String result = jsonArray.getJSONObject(0).getString("text");
                        addResponse(result.trim());
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                } else {
                    addResponse("Failed to load response due to " + response.body().toString());
                }
            }
        });

    }

    //gpt-3.5
    void callApiTurbo(String question) {

        addToChat("正在请求 骚等~", Message.SENT_BY_ROBOT);

        JSONObject jsonBody = new JSONObject();
        JSONObject messageBody = new JSONObject();
        JSONArray messageArr = new JSONArray();
        try {
            //使用模型
            jsonBody.put("model", "gpt-3.5-turbo");
            //问题关键字
            messageBody.put("role", "user");
            messageBody.put("content", question);
            messageArr.put(messageBody);
            jsonBody.put("messages", messageArr);
            //回复的最大令牌数
            jsonBody.put("max_tokens", apiData.getMaxTokens());
            jsonBody.put("temperature", apiData.getTemperature());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        //创建一个请求表单
        Log.d("guaimiao", jsonBody.toString());
        RequestBody requestBody = RequestBody.create(jsonBody.toString(), JSON);

        //创建request请求
        Request request = new Request.Builder()
                //请求地址
                .url("https://api.openai.com/v1/chat/completions")
                //api key
                .header("Authorization", "Bearer " + apiKey)
                .addHeader("Content-Type", "application/json")
                //提交表单
                .post(requestBody)
                .build();

        //提交请求 得到response返回报文
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d("guaimiao", e.getMessage());
                addResponse("Failed to load response due to " + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    try {
                        JSONObject jsonObject = new JSONObject(response.body().string());
                        Log.d("guaimiao", jsonObject.toString());
                        JSONArray choicesArray = jsonObject.getJSONArray("choices");
                        JSONObject item = choicesArray.getJSONObject(0);
                        JSONObject message = item.getJSONObject("message");
                        Log.d("guaimiao", message.toString());
                        String content = message.getString("content");
                        addResponse(content.trim());
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                } else {
                    Log.d("guaimiao", response.body().string());
                    addResponse("Failed to load response due to " + response.body().toString());
                }
            }
        });

    }

}